A walkthrough of Ernest (2005)’s original analytical approach, from close reading of the paper.

Questions

  1. Is energy use across body size categories (regardless of species) uniform or multimodal?
  1. If energy use is not uniform across body size categories, does the species level body size distribution correspond to modes of energy use?

Data

Ernest data

Ernest drew data from the Andrews LTER, the Sevilleta, Niwot Ridge, and Portal. For details of these field sites, see the relevant publications cited in Ernest (2005).

Translation to replicate-becs

The same datasets are available online, from these sources.

download_raw_paper_data(storagepath = storagepath)

Process raw data into the appropriate format. This is a data table with a record for each individual and columns for species and weight in grams. By default these tables will be stored in subdirectores of replicate-becs/data/paper/processed.

process_raw_data(storagepath = storagepath)
Loading in data version 1.106.0
[1] TRUE

Load data tables for each community.

communities <- load_paper_data(storagepath = storagepath)

Each community should be a data table with columns for species and size for each individual, for example:

names(communities)[[1]]
[1] "andrews"
head(communities[[1]])

Comparing data from 2005 to data available in 2019

Although the same datasets are now available online, they may have changed somewhat since 2005 (due to error checking, etc). Ernest (2005) may also have taken some cleaning and filtering steps, the details of which could have been omitted from the manuscript due to length restrictions. For example, many studies using data from the Portal Project omit ground squirrels, because although they may be within the “small mammal” size range, they are not target taxa for the sampling method.

Ernest (2005) reported summary statistics of her datasets; here I compare them to the corresponding dataset in the 2019 data.

summary_stats_comparison = compare_summary_stats(storagepath = storagepath)
Column `community_name` joining character vector and factor, coercing into character vector
print(summary_stats_comparison)

Constructing distributions/metrics

Body size-energy use distributions (BSED)

Ernest method

  • Per individual, calculate metabolic rate as metabolic rate \(B \propto M^\frac{3}{4}\) where \(M\) is mass in grams.
  • Sum energy use of all individuals in body size classes of .2 natural log units.
  • Also try classes of .1 and .3 natural log units
  • Convert raw energy use values for each body size class into the proportion of all the energy used in that community used by that body size class. This allows for comparisons between communities.
Ernest 2005 Fig 1

Ernest 2005 Fig 1

Translation to replicate-becs

For every individual, calculate metabolic rate and assign to a size class.

communities_energy <- lapply(communities, FUN = make_community_table, ln_units = 0.2)
head(communities_energy[[1]])

For each community, sum total energy use for each size class, and convert to the proportion of total energy use for that community.

bseds <- lapply(communities_energy, FUN = make_bsed)
head(bseds[[1]])

Species-level body size distributions (BSD)

Ernest method

  • Frequency distributions of mean mass of each species in a community.
  • For plotting (but not statistics), smoothed using kernel density estimation.
  • Gaussian kernel to mimic the actual body size distribution in log space
  • avg. std dev of the mean of the logged masses = smoothing parameter \(h\)
  • align sampling points with the midpoint of each size class in the BSED
  • after Manly 1996, “Are there clumps in body-size distributions?”, Ecology

Translation to replicate-becs

Calculate mean mass of each species in each community.

bsds <- lapply(communities, FUN = make_bsd) 
head(bsds[[1]])

Energetic dominance (\(D_E\))

  • Define “energy use modes” as contiguous body size classes where the energy use of each size class > 5% of the community total.
  • i.e. a little bit more than the expectation if energy use is uniform across all body sizes
  • Calculate the total energy use for each species in the mode.
  • Calculate the “dominance” of the species with the highest energy use in that mode as \(D_E = p_{max}\), where \(p_{max}\) is the maximum proportion of energy use by any one species in a mode.
  • “a modification of the Berger-Parker dominance index (Berger and Parker 1970)”

Translation to replicate-becs

  • Find contiguous size classes where each class has >5% of total energy use
  • Calculate the total energy use for each species, and the proportion held by the species with the highest energy use (\(p_{max}\))
  • Return \(p_{max}\) for every mode, along with the min and max size classes in that mode for each community
energetic_dom <- lapply(communities_energy, FUN = energetic_dominance) 
head(energetic_dom[[1]])
  • To plot, combine all modes from all communities and plot a histogram of \(D_E\) values.

Statistical tests

Comparing BSEDs to uniform

Ernest approach

  • Use bootstrap sampling to compare to uniform distributions.
  • For every community, draw 10000 samples (sim communities):
  • Same number of individuals as the empirical community, drawn from a uniform distribution ranging from the smallest to largest body size individual metabolic rate of any individual in that community.
  • For sim communities and the empirical community, calculate a distribution overlap index (\(DOI\)):
  • \(DOI = \sum_k {|y_{ak} - y_{bk}|}\) where \(y\) is the value for size class \(k\) in communities \(a\) and \(b\).
  • \(DOI\) values will range from 0 (complete overlap) to 2 (no overlap).
  • For the BSED bootstraps, community \(a\) is the empirical or sim distribution, and community \(b\) is a true uniform distribution (i.e. \(y_{bk} = \frac{1}{\max(k)}\) for all \(k\))
  • “True uniform distribution”: There are exactly the same number of individuals of every size.
  • Calculate the \(DOI\) for all sim communities and the empirical.
  • Find the quantile value for the empirical \(DOI\) compared to the distribution of sim \(DOI\)s. This is the p-value; i.e. the proportion of sim uniform distributions with DOIs greater than the empirical.

Translation to replicate-becs

  • For a given empirical community, draw 10000 sim communities each with the same number of individuals \(n\), with body sizes randomly drawn from a uniform distribution from the minimum to maximum body size in that community.
  • Calculate the \(DOI\) of each sim community compared to a true uniform distribution.
  • True uniform distribution = every size from the minimum to the maximum size in the community (by .1g) has exactly one individual.
bsed_uniform_bootstraps <- lapply(communities, FUN = community_bootstrap,  bootstrap_function = 'bootstrap_unif_bsed_doi', nbootstraps = 10000)

See issue #4 on github.

Compare BSEDs among communities

Ernest approach

  • For every pair of communities, create a pool of masses of all individuals from both communities.
  • Draw two new communities with the same number of individuals as the empirical communities, pulling masses at random from the pool, with replacement.
  • Calculate the DOI for the BSEDs of the two sample communities.
  • Repeat 10000 for each pair.
  • The P value is the proportion of sample DOIs greater (i.e. less overlap) than the empirical value.

Translation to replicate-becs

  • For every pair of communities, pool all the masses
  • Resample two communities of the right sizes
  • Construct BSEDs for both communities
  • Calculate the DOI of the two BSEDs
  • Repeat 10000x
community_combination_indices = utils::combn(x = c(1:9), m = 2, simplify = TRUE) %>%
  t() %>%
  as.data.frame() %>%
  dplyr::rename(community_a = V1, community_b = V2)
combine_communities = function(indices, communities) {
  community_combination = list(community_a = communities[[indices[1]]], community_b = communities[[indices[2]]], community_names = c(names(communities)[[indices[1]]], names(communities)[[indices[2]]]))
  
  return(community_combination)
}
community_combinations = apply(community_combination_indices, MARGIN = 1, FUN = combine_communities, communities = communities)
bsed_crosscomm_bootstraps = lapply(community_combinations, FUN = community_bootstrap, 
                                   bootstrap_function = 'bootstrap_crosscomm_bseds', nbootstraps = 10000)

See histogram of p values for comparisons to see if commuities’ BSEDs are the same or different.

Testing BSDs for uniformity

Ernest approach

  • \(\delta\)-corrected Kolmogorov-Smirnov test.
  • “The \(\delta\)-corrected K-S test increases the power of the test when sample sizes are small (n < 25; Zar 1999)”
  • The \(\delta\)-corrected test is not widely discussed online.
ernest_key =  read.csv(file.path(storagepath, 'ernest-2005-files', 'ernest_key.csv'), stringsAsFactors = F)
ernest_bsds_uniform_results = read.csv(file.path(storagepath, 'ernest-2005-files', 'ernest_appendixA.csv'), stringsAsFactors = F) %>%
  dplyr::left_join(ernest_key, by = 'site')
print(ernest_bsds_uniform_results)

Translation to replicate-becs:

From Zar (1999) Biostatistical Analysis.

Base K-S test
  • Take vector of measurements \(X_i\).
  • For each \(X_i\) record the observed frequency \(f_i\) (number of observations with that value).
  • Determine cumulative observed frequencies \(F_i\) and cumulative relative frequencies \(\textrm{rel}F_i\):
  • \(\textrm{rel}F_i = \frac{F_i}{n}\) where \(n\) is the number of measurements taken.
  • \(\textrm{rel}F_i\) is the proportion of the sample that is measurements \(\leq X_i\).
  • For each \(X_i\), determine the cumulative relative expected frequency from the comparison distribution, \(\textrm{rel}\hat{F_i}\).
  • For a uniform distribution, \(\textrm{rel}\hat{F_i} = \frac{X_i - \min(X)}{\max(X) - \min(X)}\)
  • Determine \(D_i\) and \(D'_i\) as:
  • \(D_i = |{\textrm{rel}F_i - \textrm{rel}\hat{F_i}}|\)
  • \(D'_i = |{\textrm{rel}F_{i-1} - \textrm{rel}\hat{F_i}}|\)
  • note \(F_0 = 0\) so \(D'_1 = \textrm{rel}\hat{F_i}\)
  • The test statistic \(D\) is:
  • \(D = \max[(\max(D_i), (\max(D'_i)]\)
  • Compare to critical values from appendix.
\(\delta\)-corrected KS test
  • For small sample sizes (<25) we can obtain increased power using the \(\delta\)-corrected KS test.
  • For each \(i\) determine
  • \(\textrm{rel}G_i = \frac{F_i}{n + 1}\)
  • \(\textrm{rel}G'_i = \frac{F_i - 1}{n - 1}\)
  • Then obtain similar \(D\)s
  • \(D_{0, i} = |\textrm{rel}G_i - \textrm{rel}\hat{F_i}|\)
  • \(D_{1, i} = |\textrm{rel}G'_i - \textrm{rel}\hat{F_i}|\)
  • The test statistic is either \(\max(D_{0, i})\) or \(\max(D_{1, i})\), whichever leads to the highest level of significance/smallest probability. Look up significance in table from appendix. The 1 and 0 are the \(\delta\)s.

Tables of critical values were entered by hand from the appendix to Zar (1999).

The \(\delta\) corrected KS test does not correspond to the results from Ernest when the species mean body size values are on an untransformed scale.

Using the natural log of the species mean body size value, however…:

With mean mass logged, all the results replicate qualitatively (i.e. not significantly different from uniform) and Niwot, for which the currently-available data most closely matches that reported in Ernest (2005), replicates almost exactly numerically.

Comparing BSDs among communities

Ernest approach

Ernest (2005) used a two-sample Kolmogorov-Smirnov test to compare every possible combination of community-level BSDs.

Translation to replicate-becs

# use same community combinations as before
bsd_crosscomm_ks = lapply(community_combinations, FUN = ks_bsd, 
                          ln_mass_vals = F)

LS0tCnRpdGxlOiAiTmFycmF0aXZlIG9mIG9yaWdpbmFsIGFuYWx5c2lzIgphdXRob3I6ICJSZW5hdGEgRGlheiIKZGF0ZTogIjUvMTQvMjAxOSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGfQpsaWJyYXJ5KHJlcGxpY2F0ZWJlY3MpCmRvd25sb2FkX2RhdGEgPSBUUlVFCnNvdXJjZV9zaW1zID0gRkFMU0UKc2V0LnNlZWQoMzUyKSAjIEdOViBhcmVhIGNvZGUsIGZvciBmdW4Kc3RvcmFnZXBhdGggPSBoZXJlOjpoZXJlKCdmaWxlcycpCnNldHVwX2ZpbGVzKHN0b3JhZ2VwYXRoID0gc3RvcmFnZXBhdGgpCmBgYAoKQSB3YWxrdGhyb3VnaCBvZiBFcm5lc3QgKDIwMDUpJ3Mgb3JpZ2luYWwgYW5hbHl0aWNhbCBhcHByb2FjaCwgZnJvbSBjbG9zZSByZWFkaW5nIG9mIHRoZSBwYXBlci4gCgojIyBRdWVzdGlvbnMKCjEuIElzIGVuZXJneSB1c2UgYWNyb3NzIGJvZHkgc2l6ZSBjYXRlZ29yaWVzIChyZWdhcmRsZXNzIG9mIHNwZWNpZXMpIHVuaWZvcm0gb3IgbXVsdGltb2RhbD8KLSB1bmlmb3JtIHdvdWxkIGNvcnJlc3BvbmQgZ2VuZXJhbGx5IHRvIGVuZXJnZXRpYyBlcXVpdmFsZW5jZS9EYW11dGgncyBydWxlLgotIG11bHRpbW9kYWwgbWlnaHQgc3VnZ2VzdCBkaWZmZXJlbnQgcmVzb3VyY2UgYXZhaWxhYmlsaXR5IGZvciBkaWZmZXJlbnQgYm9keSBzaXplcy4KMi4gSWYgZW5lcmd5IHVzZSBpcyBub3QgdW5pZm9ybSBhY3Jvc3MgYm9keSBzaXplIGNhdGVnb3JpZXMsIGRvZXMgdGhlIHNwZWNpZXMgbGV2ZWwgYm9keSBzaXplIGRpc3RyaWJ1dGlvbiBjb3JyZXNwb25kIHRvIG1vZGVzIG9mIGVuZXJneSB1c2U/Ci0gaS5lLiBhcmUgdGhlcmUgbW9yZSBzcGVjaWVzIHdpdGggbWVhbiBib2R5IHNpemVzIGFyb3VuZCB0aGUgbW9kZXMgb2YgdGhlIGJvZHkgc2l6ZS1lbmVyZ3kgdXNlIGRpc3RyaWJ1dGlvbj8KLSBpZiBzbywgbWF5YmUgaXQncyBnb29kIHRvIGJlIGNlcnRhaW4gc2l6ZXMsIGFuZCBzcGVjaWVzIGFjY3VtdWxhdGUgYXQgdGhvc2Ugb3B0aW1hLgoKCiMjIERhdGEKCiMjIyMgRXJuZXN0IGRhdGEKRXJuZXN0IGRyZXcgZGF0YSBmcm9tIHRoZSBBbmRyZXdzIExURVIsIHRoZSBTZXZpbGxldGEsIE5pd290IFJpZGdlLCBhbmQgUG9ydGFsLiBGb3IgZGV0YWlscyBvZiB0aGVzZSBmaWVsZCBzaXRlcywgc2VlIHRoZSBfcmVsZXZhbnQgcHVibGljYXRpb25zIGNpdGVkIGluIEVybmVzdCAoMjAwNSkuXyAKCiMjIyMgVHJhbnNsYXRpb24gdG8gYHJlcGxpY2F0ZS1iZWNzYAoKVGhlIHNhbWUgZGF0YXNldHMgYXJlIGF2YWlsYWJsZSBvbmxpbmUsIF9mcm9tIHRoZXNlIHNvdXJjZXNfLiAKYGBge3IgZG93bmxvYWQgZGF0YSBpZiBub3QgZG93bmxvYWRlZCwgaW5jbHVkZSA9IEZ9CmlmKGRvd25sb2FkX2RhdGEpIGRvd25sb2FkX3Jhd19wYXBlcl9kYXRhKHN0b3JhZ2VwYXRoID0gc3RvcmFnZXBhdGgpCmBgYAoKYGBge3IgZG93bmxvYWQgcmF3IGRhdGEsIGV2YWwgPSBGfQpkb3dubG9hZF9yYXdfcGFwZXJfZGF0YShzdG9yYWdlcGF0aCA9IHN0b3JhZ2VwYXRoKQpgYGAKClByb2Nlc3MgcmF3IGRhdGEgaW50byB0aGUgYXBwcm9wcmlhdGUgZm9ybWF0LiBUaGlzIGlzIGEgZGF0YSB0YWJsZSB3aXRoIGEgcmVjb3JkIGZvciBlYWNoIGluZGl2aWR1YWwgYW5kIGNvbHVtbnMgZm9yIGBzcGVjaWVzYCBhbmQgYHdlaWdodGAgaW4gZ3JhbXMuIEJ5IGRlZmF1bHQgdGhlc2UgdGFibGVzIHdpbGwgYmUgc3RvcmVkIGluIHN1YmRpcmVjdG9yZXMgb2YgYHJlcGxpY2F0ZS1iZWNzL2RhdGEvcGFwZXIvcHJvY2Vzc2VkYC4gCgpgYGB7ciBwcm9jZXNzIHBhcGVyIGRhdGF9CnByb2Nlc3NfcmF3X2RhdGEoc3RvcmFnZXBhdGggPSBzdG9yYWdlcGF0aCkKYGBgCgpMb2FkIGRhdGEgdGFibGVzIGZvciBlYWNoIGNvbW11bml0eS4gCgpgYGB7ciBsb2FkIGNvbW11bml0eSBkYXRhLCBlY2hvPVRSVUV9CmNvbW11bml0aWVzIDwtIGxvYWRfcGFwZXJfZGF0YShzdG9yYWdlcGF0aCA9IHN0b3JhZ2VwYXRoKQpgYGAKCkVhY2ggY29tbXVuaXR5IHNob3VsZCBiZSBhIGRhdGEgdGFibGUgd2l0aCBjb2x1bW5zIGZvciBzcGVjaWVzIGFuZCBzaXplIGZvciBlYWNoIGluZGl2aWR1YWwsIGZvciBleGFtcGxlOgoKYGBge3IgaW5zcGVjdCBjb21tdW5pdHkgZGF0YX0KbmFtZXMoY29tbXVuaXRpZXMpW1sxXV0KaGVhZChjb21tdW5pdGllc1tbMV1dKQpgYGAKCgojIyMjIENvbXBhcmluZyBkYXRhIGZyb20gMjAwNSB0byBkYXRhIGF2YWlsYWJsZSBpbiAyMDE5CgpBbHRob3VnaCB0aGUgc2FtZSBkYXRhc2V0cyBhcmUgbm93IGF2YWlsYWJsZSBvbmxpbmUsIHRoZXkgbWF5IGhhdmUgY2hhbmdlZCBzb21ld2hhdCBzaW5jZSAyMDA1IChkdWUgdG8gZXJyb3IgY2hlY2tpbmcsIGV0YykuIEVybmVzdCAoMjAwNSkgbWF5IGFsc28gaGF2ZSB0YWtlbiBzb21lIGNsZWFuaW5nIGFuZCBmaWx0ZXJpbmcgc3RlcHMsIHRoZSBkZXRhaWxzIG9mIHdoaWNoIGNvdWxkIGhhdmUgYmVlbiBvbWl0dGVkIGZyb20gdGhlIG1hbnVzY3JpcHQgZHVlIHRvIGxlbmd0aCByZXN0cmljdGlvbnMuIEZvciBleGFtcGxlLCBtYW55IHN0dWRpZXMgdXNpbmcgZGF0YSBmcm9tIHRoZSBQb3J0YWwgUHJvamVjdCBvbWl0IGdyb3VuZCBzcXVpcnJlbHMsIGJlY2F1c2UgYWx0aG91Z2ggdGhleSBtYXkgYmUgd2l0aGluIHRoZSAic21hbGwgbWFtbWFsIiBzaXplIHJhbmdlLCB0aGV5IGFyZSBub3QgdGFyZ2V0IHRheGEgZm9yIHRoZSBzYW1wbGluZyBtZXRob2QuIAoKRXJuZXN0ICgyMDA1KSByZXBvcnRlZCBzdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgaGVyIGRhdGFzZXRzOyBoZXJlIEkgY29tcGFyZSB0aGVtIHRvIHRoZSBjb3JyZXNwb25kaW5nIGRhdGFzZXQgaW4gdGhlIDIwMTkgZGF0YS4gCgpgYGB7ciBjb21wYXJlIHN1bW1hcnkgc3RhdHN9CnN1bW1hcnlfc3RhdHNfY29tcGFyaXNvbiA9IGNvbXBhcmVfc3VtbWFyeV9zdGF0cyhzdG9yYWdlcGF0aCA9IHN0b3JhZ2VwYXRoKQoKcHJpbnQoc3VtbWFyeV9zdGF0c19jb21wYXJpc29uKQpgYGAKCgojIyBDb25zdHJ1Y3RpbmcgZGlzdHJpYnV0aW9ucy9tZXRyaWNzCgojIyMgQm9keSBzaXplLWVuZXJneSB1c2UgZGlzdHJpYnV0aW9ucyAoQlNFRCkKCiMjIyMgRXJuZXN0IG1ldGhvZAoKLSBQZXIgaW5kaXZpZHVhbCwgY2FsY3VsYXRlIG1ldGFib2xpYyByYXRlIGFzIG1ldGFib2xpYyByYXRlICRCIFxwcm9wdG8gTV5cZnJhY3szfXs0fSQgd2hlcmUgJE0kIGlzIG1hc3MgaW4gZ3JhbXMuCi0gU3VtIGVuZXJneSB1c2Ugb2YgYWxsIGluZGl2aWR1YWxzIGluIGJvZHkgc2l6ZSBjbGFzc2VzIG9mIC4yIG5hdHVyYWwgbG9nIHVuaXRzLgotIEFsc28gdHJ5IGNsYXNzZXMgb2YgLjEgYW5kIC4zIG5hdHVyYWwgbG9nIHVuaXRzCi0gQ29udmVydCByYXcgZW5lcmd5IHVzZSB2YWx1ZXMgZm9yIGVhY2ggYm9keSBzaXplIGNsYXNzIGludG8gdGhlIHByb3BvcnRpb24gb2YgYWxsIHRoZSBlbmVyZ3kgdXNlZCBpbiB0aGF0IGNvbW11bml0eSB1c2VkIGJ5IHRoYXQgYm9keSBzaXplIGNsYXNzLiBUaGlzIGFsbG93cyBmb3IgY29tcGFyaXNvbnMgYmV0d2VlbiBjb21tdW5pdGllcy4KCgohW0VybmVzdCAyMDA1IEZpZyAxXSguLi9hbmFseXNpcy9lcm5lc3QyMDA1X2ZpZzEucG5nKQoKCiMjIyMgVHJhbnNsYXRpb24gdG8gYHJlcGxpY2F0ZS1iZWNzYAoKRm9yIGV2ZXJ5IGluZGl2aWR1YWwsIGNhbGN1bGF0ZSBtZXRhYm9saWMgcmF0ZSBhbmQgYXNzaWduIHRvIGEgc2l6ZSBjbGFzcy4gCgpgYGB7ciBjb25zdHJ1Y3QgQlNFRHN9CmNvbW11bml0aWVzX2VuZXJneSA8LSBsYXBwbHkoY29tbXVuaXRpZXMsIEZVTiA9IG1ha2VfY29tbXVuaXR5X3RhYmxlLCBsbl91bml0cyA9IDAuMikKCmhlYWQoY29tbXVuaXRpZXNfZW5lcmd5W1sxXV0pCmBgYAoKRm9yIGVhY2ggY29tbXVuaXR5LCBzdW0gdG90YWwgZW5lcmd5IHVzZSBmb3IgZWFjaCBzaXplIGNsYXNzLCBhbmQgY29udmVydCB0byB0aGUgcHJvcG9ydGlvbiBvZiB0b3RhbCBlbmVyZ3kgdXNlIGZvciB0aGF0IGNvbW11bml0eS4KCmBgYHtyIG1ha2UgYnNlZHN9CmJzZWRzIDwtIGxhcHBseShjb21tdW5pdGllc19lbmVyZ3ksIEZVTiA9IG1ha2VfYnNlZCkKCmhlYWQoYnNlZHNbWzFdXSkKYGBgCgpgYGB7ciBwbG90IGJzZWRzLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9Cgpic2Vkc19wbG90IDwtIHBsb3RfcGFwZXJfZGlzdHMoYnNlZHMsIGRpc3RfdHlwZSA9ICdic2VkJykKCmludmlzaWJsZShic2Vkc19wbG90KQpgYGAKCiMjIyBTcGVjaWVzLWxldmVsIGJvZHkgc2l6ZSBkaXN0cmlidXRpb25zIChCU0QpCgojIyMjIEVybmVzdCBtZXRob2QKLSBGcmVxdWVuY3kgZGlzdHJpYnV0aW9ucyBvZiBtZWFuIG1hc3Mgb2YgZWFjaCBzcGVjaWVzIGluIGEgY29tbXVuaXR5LgotIEZvciBwbG90dGluZyAoYnV0IG5vdCBzdGF0aXN0aWNzKSwgc21vb3RoZWQgdXNpbmcga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbi4gCi0gR2F1c3NpYW4ga2VybmVsIHRvIG1pbWljIHRoZSBhY3R1YWwgYm9keSBzaXplIGRpc3RyaWJ1dGlvbiBpbiBsb2cgc3BhY2UKLSBhdmcuIHN0ZCBkZXYgb2YgdGhlIG1lYW4gb2YgdGhlIGxvZ2dlZCBtYXNzZXMgPSBzbW9vdGhpbmcgcGFyYW1ldGVyICRoJAotIGFsaWduIHNhbXBsaW5nIHBvaW50cyB3aXRoIHRoZSBtaWRwb2ludCBvZiBlYWNoIHNpemUgY2xhc3MgaW4gdGhlIEJTRUQKLSBhZnRlciBNYW5seSAxOTk2LCAiQXJlIHRoZXJlIGNsdW1wcyBpbiBib2R5LXNpemUgZGlzdHJpYnV0aW9ucz8iLCBfRWNvbG9neV8KCiMjIyMgVHJhbnNsYXRpb24gdG8gYHJlcGxpY2F0ZS1iZWNzYAoKQ2FsY3VsYXRlIG1lYW4gbWFzcyBvZiBlYWNoIHNwZWNpZXMgaW4gZWFjaCBjb21tdW5pdHkuIAoKYGBge3IgY29uc3RydWN0IGJzZHN9IAoKYnNkcyA8LSBsYXBwbHkoY29tbXVuaXRpZXMsIEZVTiA9IG1ha2VfYnNkKSAKCmhlYWQoYnNkc1tbMV1dKQpgYGAKCmBgYHtyIHBsb3QgYnNkcywgZWNobz1GQUxTRSwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQoKYnNkc19wbG90IDwtIHBsb3RfcGFwZXJfZGlzdHMoYnNkcywgZGlzdF90eXBlID0gJ2JzZCcpCgppbnZpc2libGUoYnNkc19wbG90KQpgYGAKCgojIyMgRW5lcmdldGljIGRvbWluYW5jZSAoJERfRSQpCgotIERlZmluZSAiZW5lcmd5IHVzZSBtb2RlcyIgYXMgY29udGlndW91cyBib2R5IHNpemUgY2xhc3NlcyB3aGVyZSB0aGUgZW5lcmd5IHVzZSBvZiBlYWNoIHNpemUgY2xhc3MgPiA1JSBvZiB0aGUgY29tbXVuaXR5IHRvdGFsLiAKLSBpLmUuIGEgbGl0dGxlIGJpdCBtb3JlIHRoYW4gdGhlIGV4cGVjdGF0aW9uIGlmIGVuZXJneSB1c2UgaXMgdW5pZm9ybSBhY3Jvc3MgYWxsIGJvZHkgc2l6ZXMKLSBDYWxjdWxhdGUgdGhlIHRvdGFsIGVuZXJneSB1c2UgZm9yIGVhY2ggc3BlY2llcyBpbiB0aGUgbW9kZS4gCi0gQ2FsY3VsYXRlIHRoZSAiZG9taW5hbmNlIiBvZiB0aGUgc3BlY2llcyB3aXRoIHRoZSBoaWdoZXN0IGVuZXJneSB1c2UgaW4gdGhhdCBtb2RlIGFzICREX0UgPSBwX3ttYXh9JCwgd2hlcmUgJHBfe21heH0kIGlzIHRoZSBtYXhpbXVtIHByb3BvcnRpb24gb2YgZW5lcmd5IHVzZSBieSBhbnkgb25lIHNwZWNpZXMgaW4gYSBtb2RlLiAKLSAiYSBtb2RpZmljYXRpb24gb2YgdGhlIEJlcmdlci1QYXJrZXIgZG9taW5hbmNlIGluZGV4IChCZXJnZXIgYW5kIFBhcmtlciAxOTcwKSIKCiMjIyMgVHJhbnNsYXRpb24gdG8gYHJlcGxpY2F0ZS1iZWNzYAoKLSBGaW5kIGNvbnRpZ3VvdXMgc2l6ZSBjbGFzc2VzIHdoZXJlIGVhY2ggY2xhc3MgaGFzID41JSBvZiB0b3RhbCBlbmVyZ3kgdXNlCi0gQ2FsY3VsYXRlIHRoZSB0b3RhbCBlbmVyZ3kgdXNlIGZvciBlYWNoIHNwZWNpZXMsIGFuZCB0aGUgcHJvcG9ydGlvbiBoZWxkIGJ5IHRoZSBzcGVjaWVzIHdpdGggdGhlIGhpZ2hlc3QgZW5lcmd5IHVzZSAoJHBfe21heH0kKQotIFJldHVybiAkcF97bWF4fSQgZm9yIGV2ZXJ5IG1vZGUsIGFsb25nIHdpdGggdGhlIG1pbiBhbmQgbWF4IHNpemUgY2xhc3NlcyBpbiB0aGF0IG1vZGUgZm9yIGVhY2ggY29tbXVuaXR5CgpgYGB7ciBlbmVyZ2V0aWMgZG9taW5hbmNlfQoKZW5lcmdldGljX2RvbSA8LSBsYXBwbHkoY29tbXVuaXRpZXNfZW5lcmd5LCBGVU4gPSBlbmVyZ2V0aWNfZG9taW5hbmNlKSAKCmhlYWQoZW5lcmdldGljX2RvbVtbMV1dKQoKYGBgCgotIFRvIHBsb3QsIGNvbWJpbmUgYWxsIG1vZGVzIGZyb20gYWxsIGNvbW11bml0aWVzIGFuZCBwbG90IGEgaGlzdG9ncmFtIG9mICREX0UkIHZhbHVlcy4KCmBgYHtyIHBsb3QgRWQsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTV9CmVfZG9tX3Bsb3QgPC0gcGxvdF9lX2RvbShlbmVyZ2V0aWNfZG9tKQplX2RvbV9wbG90CmBgYAoKCiMjIFN0YXRpc3RpY2FsIHRlc3RzCgojIyMgQ29tcGFyaW5nIEJTRURzIHRvIHVuaWZvcm0KCiMjIyMgRXJuZXN0IGFwcHJvYWNoCgotIFVzZSBib290c3RyYXAgc2FtcGxpbmcgdG8gY29tcGFyZSB0byB1bmlmb3JtIGRpc3RyaWJ1dGlvbnMuCi0gRm9yIGV2ZXJ5IGNvbW11bml0eSwgZHJhdyAxMDAwMCBzYW1wbGVzIChzaW0gY29tbXVuaXRpZXMpOgotIFNhbWUgbnVtYmVyIG9mIGluZGl2aWR1YWxzIGFzIHRoZSBlbXBpcmljYWwgY29tbXVuaXR5LCBkcmF3biBmcm9tIGEgdW5pZm9ybSBkaXN0cmlidXRpb24gcmFuZ2luZyBmcm9tIHRoZSBzbWFsbGVzdCB0byBsYXJnZXN0IH5+Ym9keSBzaXplfn4gaW5kaXZpZHVhbCBtZXRhYm9saWMgcmF0ZSBvZiBhbnkgaW5kaXZpZHVhbCBpbiB0aGF0IGNvbW11bml0eS4KLSBGb3Igc2ltIGNvbW11bml0aWVzIGFuZCB0aGUgZW1waXJpY2FsIGNvbW11bml0eSwgY2FsY3VsYXRlIGEgZGlzdHJpYnV0aW9uIG92ZXJsYXAgaW5kZXggKCRET0kkKToKLSAkRE9JID0gXHN1bV9rIHt8eV97YWt9IC0geV97Ymt9fH0kIHdoZXJlICR5JCBpcyB0aGUgdmFsdWUgZm9yIHNpemUgY2xhc3MgJGskIGluIGNvbW11bml0aWVzICRhJCBhbmQgJGIkLgotICRET0kkIHZhbHVlcyB3aWxsIHJhbmdlIGZyb20gMCAoY29tcGxldGUgb3ZlcmxhcCkgdG8gMiAobm8gb3ZlcmxhcCkuIAotIEZvciB0aGUgQlNFRCBib290c3RyYXBzLCBjb21tdW5pdHkgJGEkIGlzIHRoZSBlbXBpcmljYWwgb3Igc2ltIGRpc3RyaWJ1dGlvbiwgYW5kIGNvbW11bml0eSAkYiQgaXMgYSB0cnVlIHVuaWZvcm0gZGlzdHJpYnV0aW9uIH5+KGkuZS4gJHlfe2JrfSA9IFxmcmFjezF9e1xtYXgoayl9JCBmb3IgYWxsICRrJCl+fgotICJUcnVlIHVuaWZvcm0gZGlzdHJpYnV0aW9uIjogVGhlcmUgYXJlIGV4YWN0bHkgdGhlIHNhbWUgbnVtYmVyIG9mIGluZGl2aWR1YWxzIG9mIGV2ZXJ5IHNpemUuIAotIENhbGN1bGF0ZSB0aGUgJERPSSQgZm9yIGFsbCBzaW0gY29tbXVuaXRpZXMgYW5kIHRoZSBlbXBpcmljYWwuCi0gRmluZCB0aGUgcXVhbnRpbGUgdmFsdWUgZm9yIHRoZSBlbXBpcmljYWwgJERPSSQgY29tcGFyZWQgdG8gdGhlIGRpc3RyaWJ1dGlvbiBvZiBzaW0gJERPSSRzLiBUaGlzIGlzIHRoZSBwLXZhbHVlOyBpLmUuIHRoZSBwcm9wb3J0aW9uIG9mIHNpbSB1bmlmb3JtIGRpc3RyaWJ1dGlvbnMgd2l0aCBET0lzIGdyZWF0ZXIgdGhhbiB0aGUgZW1waXJpY2FsLgoKIyMjIyBUcmFuc2xhdGlvbiB0byBgcmVwbGljYXRlLWJlY3NgCgotIEZvciBhIGdpdmVuIGVtcGlyaWNhbCBjb21tdW5pdHksIGRyYXcgMTAwMDAgc2ltIGNvbW11bml0aWVzIGVhY2ggd2l0aCB0aGUgc2FtZSBudW1iZXIgb2YgaW5kaXZpZHVhbHMgJG4kLCB3aXRoIGJvZHkgc2l6ZXMgcmFuZG9tbHkgZHJhd24gZnJvbSBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uIGZyb20gdGhlIG1pbmltdW0gdG8gbWF4aW11bSBib2R5IHNpemUgaW4gdGhhdCBjb21tdW5pdHkuCi0gQ2FsY3VsYXRlIHRoZSAkRE9JJCBvZiBlYWNoIHNpbSBjb21tdW5pdHkgY29tcGFyZWQgdG8gYSB0cnVlIHVuaWZvcm0gZGlzdHJpYnV0aW9uLiAKLSBUcnVlIHVuaWZvcm0gZGlzdHJpYnV0aW9uID0gZXZlcnkgc2l6ZSBmcm9tIHRoZSBtaW5pbXVtIHRvIHRoZSBtYXhpbXVtIHNpemUgaW4gdGhlIGNvbW11bml0eSAoYnkgLjFnKSBoYXMgZXhhY3RseSBvbmUgaW5kaXZpZHVhbC4KCmBgYHtyIEJTRUQtdW5pZm9ybSBib290c3RyYXBwZWQgRE9JcywgZXZhbCA9IEZ9Cgpic2VkX3VuaWZvcm1fYm9vdHN0cmFwcyA8LSBsYXBwbHkoY29tbXVuaXRpZXMsIEZVTiA9IGNvbW11bml0eV9ib290c3RyYXAsICBib290c3RyYXBfZnVuY3Rpb24gPSAnYm9vdHN0cmFwX3VuaWZfYnNlZF9kb2knLCBuYm9vdHN0cmFwcyA9IDEwMDAwKQoKYGBgCgpgYGB7ciBzb3VyY2Ugb3IgZHJhdyBCU0VEIHVuaWZvcm0gYm9vdHN0cmFwcywgaW5jbHVkZSA9IEZ9CmlmKHNvdXJjZV9zaW1zKSB7CiAgbG9hZChmaWxlLnBhdGgoc3RvcmFnZXBhdGgsICdkYXRhJywgJ3NpbXMnLCAnYnNlZF91bmlmb3JtX2Jvb3RzdHJhcHMuUmRhdGEnKSkKfSBlbHNlIHsKICAKYnNlZF91bmlmb3JtX2Jvb3RzdHJhcHMgPC0gbGFwcGx5KGNvbW11bml0aWVzLCBGVU4gPSBjb21tdW5pdHlfYm9vdHN0cmFwLCAgYm9vdHN0cmFwX2Z1bmN0aW9uID0gJ2Jvb3RzdHJhcF91bmlmX2JzZWRfZG9pJywgbmJvb3RzdHJhcHMgPSAxMCkKCnNhdmUoYnNlZF91bmlmb3JtX2Jvb3RzdHJhcHMsIGZpbGUgPSAoZmlsZS5wYXRoKHN0b3JhZ2VwYXRoLCAnZGF0YScsICdzaW1zJywgJ2JzZWRfdW5pZm9ybV9ib290c3RyYXBzLlJkYXRhJykpKQp9CmBgYAoKCl9TZWUgaXNzdWUgIzQgb24gZ2l0aHViLl8KCgpgYGB7ciBwbG90IEJTRUQtdW5pZm9ybSBib290c3RyYXAgRE9JcyB2IGVtcGlyaWNhbCxlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9IAoKYnNlZF91bmlmb3JtX2Jvb3RzdHJhcF9wbG90IDwtIHBsb3RfcGFwZXJfZGlzdHMoYnNlZF91bmlmb3JtX2Jvb3RzdHJhcHMsIGRpc3RfdHlwZSA9ICdic2VkX2Jvb3RzdHJhcHMnKQoKaW52aXNpYmxlKGJzZWRfdW5pZm9ybV9ib290c3RyYXBfcGxvdCkKYGBgCgojIyMgQ29tcGFyZSBCU0VEcyBhbW9uZyBjb21tdW5pdGllcwoKIyMjIyBFcm5lc3QgYXBwcm9hY2gKLSBGb3IgZXZlcnkgcGFpciBvZiBjb21tdW5pdGllcywgY3JlYXRlIGEgcG9vbCBvZiBtYXNzZXMgb2YgYWxsIGluZGl2aWR1YWxzIGZyb20gYm90aCBjb21tdW5pdGllcy4KLSBEcmF3IHR3byBuZXcgY29tbXVuaXRpZXMgd2l0aCB0aGUgc2FtZSBudW1iZXIgb2YgaW5kaXZpZHVhbHMgYXMgdGhlIGVtcGlyaWNhbCBjb21tdW5pdGllcywgcHVsbGluZyBtYXNzZXMgYXQgcmFuZG9tIGZyb20gdGhlIHBvb2wsIHdpdGggcmVwbGFjZW1lbnQuCi0gQ2FsY3VsYXRlIHRoZSBET0kgZm9yIHRoZSBCU0VEcyBvZiB0aGUgdHdvIHNhbXBsZSBjb21tdW5pdGllcy4KLSBSZXBlYXQgMTAwMDAgZm9yIGVhY2ggcGFpci4KLSBUaGUgUCB2YWx1ZSBpcyB0aGUgcHJvcG9ydGlvbiBvZiBzYW1wbGUgRE9JcyBncmVhdGVyIChpLmUuIGxlc3Mgb3ZlcmxhcCkgdGhhbiB0aGUgZW1waXJpY2FsIHZhbHVlLiAKCiMjIyMgVHJhbnNsYXRpb24gdG8gYHJlcGxpY2F0ZS1iZWNzYAotIEZvciBldmVyeSBwYWlyIG9mIGNvbW11bml0aWVzLCBwb29sIGFsbCB0aGUgbWFzc2VzCi0gUmVzYW1wbGUgdHdvIGNvbW11bml0aWVzIG9mIHRoZSByaWdodCBzaXplcwotIENvbnN0cnVjdCBCU0VEcyBmb3IgYm90aCBjb21tdW5pdGllcwotIENhbGN1bGF0ZSB0aGUgRE9JIG9mIHRoZSB0d28gQlNFRHMKLSBSZXBlYXQgMTAwMDB4CgpgYGB7ciBwYWlycyBmb3IgY3Jvc3Njb21tdW5pdHkgQlNFRCBjb21wYXJpc29uc30KY29tbXVuaXR5X2NvbWJpbmF0aW9uX2luZGljZXMgPSB1dGlsczo6Y29tYm4oeCA9IGMoMTo5KSwgbSA9IDIsIHNpbXBsaWZ5ID0gVFJVRSkgJT4lCiAgdCgpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBkcGx5cjo6cmVuYW1lKGNvbW11bml0eV9hID0gVjEsIGNvbW11bml0eV9iID0gVjIpCgpjb21iaW5lX2NvbW11bml0aWVzID0gZnVuY3Rpb24oaW5kaWNlcywgY29tbXVuaXRpZXMpIHsKICBjb21tdW5pdHlfY29tYmluYXRpb24gPSBsaXN0KGNvbW11bml0eV9hID0gY29tbXVuaXRpZXNbW2luZGljZXNbMV1dXSwgY29tbXVuaXR5X2IgPSBjb21tdW5pdGllc1tbaW5kaWNlc1syXV1dLCBjb21tdW5pdHlfbmFtZXMgPSBjKG5hbWVzKGNvbW11bml0aWVzKVtbaW5kaWNlc1sxXV1dLCBuYW1lcyhjb21tdW5pdGllcylbW2luZGljZXNbMl1dXSkpCiAgCiAgcmV0dXJuKGNvbW11bml0eV9jb21iaW5hdGlvbikKfQoKY29tbXVuaXR5X2NvbWJpbmF0aW9ucyA9IGFwcGx5KGNvbW11bml0eV9jb21iaW5hdGlvbl9pbmRpY2VzLCBNQVJHSU4gPSAxLCBGVU4gPSBjb21iaW5lX2NvbW11bml0aWVzLCBjb21tdW5pdGllcyA9IGNvbW11bml0aWVzKQoKYGBgCgpgYGB7ciBjcm9zcyBjb21tdW5pdHkgQlNFRCBjb21wYXJpc29ucywgZXZhbCA9Rn0KCmJzZWRfY3Jvc3Njb21tX2Jvb3RzdHJhcHMgPSBsYXBwbHkoY29tbXVuaXR5X2NvbWJpbmF0aW9ucywgRlVOID0gY29tbXVuaXR5X2Jvb3RzdHJhcCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYm9vdHN0cmFwX2Z1bmN0aW9uID0gJ2Jvb3RzdHJhcF9jcm9zc2NvbW1fYnNlZHMnLCBuYm9vdHN0cmFwcyA9IDEwMDAwKQoKCmBgYAoKCmBgYHtyIHNvdXJjZSBvciBkcmF3IGNyb3NzIGNvbW11bml0eSBCU0VELCBpbmNsdWRlID0gRn0KaWYoc291cmNlX3NpbXMpIHsKICBsb2FkKGZpbGUucGF0aChzdG9yYWdlcGF0aCwgJ2RhdGEnLCAnc2ltcycsICdic2VkX2Nyb3NzY29tbV9ib290c3RyYXBzLlJkYXRhJykpCn0gZWxzZSB7CiAgCmJzZWRfY3Jvc3Njb21tX2Jvb3RzdHJhcHMgPSBsYXBwbHkoY29tbXVuaXR5X2NvbWJpbmF0aW9ucywgRlVOID0gY29tbXVuaXR5X2Jvb3RzdHJhcCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYm9vdHN0cmFwX2Z1bmN0aW9uID0gJ2Jvb3RzdHJhcF9jcm9zc2NvbW1fYnNlZHMnLCBuYm9vdHN0cmFwcyA9IDEwKQoKc2F2ZShic2VkX2Nyb3NzY29tbV9ib290c3RyYXBzLCBmaWxlID0gKGZpbGUucGF0aChzdG9yYWdlcGF0aCwgJ2RhdGEnLCAnc2ltcycsICdic2VkX2Nyb3NzY29tbV9ib290c3RyYXBzLlJkYXRhJykpKQp9CmBgYAoKYGBge3IgcGxvdCBjcm9zcyBjb21tdW5pdHkgY29tcGFyaXNvbnMsIGVjaG8gPSBGLCBmaWcuaGVpZ2h0PTMwLCBmaWcud2lkdGg9MTB9Cgpjcm9zc2NvbW1fYm9vdHN0cmFwX3Bsb3QgPSBwbG90X2Nyb3NzY29tbV9ic2Vkcyhic2VkX2Nyb3NzY29tbV9ib290c3RyYXBzKQoKaW52aXNpYmxlKGNyb3NzY29tbV9ib290c3RyYXBfcGxvdCkKCmBgYAoKYGBge3IgcGxvdCBjcm9zcyBjb21tdW5pdHkgcCB2YWx1ZXMsIGVjaG8gPSBGLCBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gNX0KcHZhbHNfaGlzdG9ncmFtID0gcGxvdF9ib290c3RyYXBfcHZhbHMoYnNlZF9jcm9zc2NvbW1fYm9vdHN0cmFwcykKCnB2YWxzX2hpc3RvZ3JhbQpgYGAKCgpTZWUgaGlzdG9ncmFtIG9mIHAgdmFsdWVzIGZvciBjb21wYXJpc29ucyB0byBzZWUgaWYgY29tbXVpdGllcycgQlNFRHMgYXJlIHRoZSBzYW1lIG9yIGRpZmZlcmVudC4KCiMjIyBUZXN0aW5nIEJTRHMgZm9yIHVuaWZvcm1pdHkKCiMjIyMgRXJuZXN0IGFwcHJvYWNoCi0gJFxkZWx0YSQtY29ycmVjdGVkIEtvbG1vZ29yb3YtU21pcm5vdiB0ZXN0LiAKLSAiVGhlICRcZGVsdGEkLWNvcnJlY3RlZCBLLVMgdGVzdCBpbmNyZWFzZXMgdGhlIHBvd2VyIG9mIHRoZSB0ZXN0IHdoZW4gc2FtcGxlIHNpemVzIGFyZSBzbWFsbCAobiA8IDI1OyBaYXIgMTk5OSkiCi0gVGhlICRcZGVsdGEkLWNvcnJlY3RlZCB0ZXN0IGlzIG5vdCB3aWRlbHkgZGlzY3Vzc2VkIG9ubGluZS4gCgoKYGBge3IgZXJuZXN0IGQga3MgdGVzdCByZXN1bHRzfQplcm5lc3Rfa2V5ID0gIHJlYWQuY3N2KGZpbGUucGF0aChzdG9yYWdlcGF0aCwgJ2VybmVzdC0yMDA1LWZpbGVzJywgJ2VybmVzdF9rZXkuY3N2JyksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQplcm5lc3RfYnNkc191bmlmb3JtX3Jlc3VsdHMgPSByZWFkLmNzdihmaWxlLnBhdGgoc3RvcmFnZXBhdGgsICdlcm5lc3QtMjAwNS1maWxlcycsICdlcm5lc3RfYXBwZW5kaXhBLmNzdicpLCBzdHJpbmdzQXNGYWN0b3JzID0gRikgJT4lCiAgZHBseXI6OmxlZnRfam9pbihlcm5lc3Rfa2V5LCBieSA9ICdzaXRlJykKcHJpbnQoZXJuZXN0X2JzZHNfdW5pZm9ybV9yZXN1bHRzKQpgYGAKCgojIyMjIFRyYW5zbGF0aW9uIHRvIGByZXBsaWNhdGUtYmVjc2A6CgoKKkZyb20gWmFyICgxOTk5KSBfQmlvc3RhdGlzdGljYWwgQW5hbHlzaXNfLioKCiMjIyMjIEJhc2UgSy1TIHRlc3QKLSBUYWtlIHZlY3RvciBvZiBtZWFzdXJlbWVudHMgJFhfaSQuIAotIEZvciBlYWNoICRYX2kkIHJlY29yZCB0aGUgb2JzZXJ2ZWQgZnJlcXVlbmN5ICRmX2kkIChudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHdpdGggdGhhdCB2YWx1ZSkuCi0gRGV0ZXJtaW5lIGN1bXVsYXRpdmUgb2JzZXJ2ZWQgZnJlcXVlbmNpZXMgJEZfaSQgYW5kIGN1bXVsYXRpdmUgcmVsYXRpdmUgZnJlcXVlbmNpZXMgJFx0ZXh0cm17cmVsfUZfaSQ6Ci0gJFx0ZXh0cm17cmVsfUZfaSA9IFxmcmFje0ZfaX17bn0kIHdoZXJlICRuJCBpcyB0aGUgbnVtYmVyIG9mIG1lYXN1cmVtZW50cyB0YWtlbi4gCi0gJFx0ZXh0cm17cmVsfUZfaSQgaXMgdGhlIHByb3BvcnRpb24gb2YgdGhlIHNhbXBsZSB0aGF0IGlzIG1lYXN1cmVtZW50cyAkXGxlcSBYX2kkLiAKLSBGb3IgZWFjaCAkWF9pJCwgZGV0ZXJtaW5lIHRoZSBjdW11bGF0aXZlICpyZWxhdGl2ZSogZXhwZWN0ZWQgZnJlcXVlbmN5IGZyb20gdGhlIGNvbXBhcmlzb24gZGlzdHJpYnV0aW9uLCAkXHRleHRybXtyZWx9XGhhdHtGX2l9JC4KLSBGb3IgYSB1bmlmb3JtIGRpc3RyaWJ1dGlvbiwgJFx0ZXh0cm17cmVsfVxoYXR7Rl9pfSA9IFxmcmFje1hfaSAtIFxtaW4oWCl9e1xtYXgoWCkgLSBcbWluKFgpfSQKLSBEZXRlcm1pbmUgJERfaSQgYW5kICREJ19pJCBhczoKLSAkRF9pID0gfHtcdGV4dHJte3JlbH1GX2kgLSBcdGV4dHJte3JlbH1caGF0e0ZfaX19fCQKLSAkRCdfaSA9IHx7XHRleHRybXtyZWx9Rl97aS0xfSAtIFx0ZXh0cm17cmVsfVxoYXR7Rl9pfX18JAotIG5vdGUgJEZfMCA9IDAkIHNvICREJ18xID0gXHRleHRybXtyZWx9XGhhdHtGX2l9JAotIFRoZSB0ZXN0IHN0YXRpc3RpYyAkRCQgaXM6Ci0gJEQgPSBcbWF4WyhcbWF4KERfaSksIChcbWF4KEQnX2kpXSQKLSBDb21wYXJlIHRvIGNyaXRpY2FsIHZhbHVlcyBmcm9tIGFwcGVuZGl4LgoKCiMjIyMjICRcZGVsdGEkLWNvcnJlY3RlZCBLUyB0ZXN0CgotIEZvciBzbWFsbCBzYW1wbGUgc2l6ZXMgKDwyNSkgd2UgY2FuIG9idGFpbiBpbmNyZWFzZWQgcG93ZXIgdXNpbmcgdGhlICRcZGVsdGEkLWNvcnJlY3RlZCBLUyB0ZXN0LgotIEZvciBlYWNoICRpJCBkZXRlcm1pbmUKLSAkXHRleHRybXtyZWx9R19pID0gXGZyYWN7Rl9pfXtuICsgMX0kCi0gJFx0ZXh0cm17cmVsfUcnX2kgPSBcZnJhY3tGX2kgLSAxfXtuIC0gMX0kCi0gVGhlbiBvYnRhaW4gc2ltaWxhciAkRCRzCi0gJERfezAsIGl9ID0gfFx0ZXh0cm17cmVsfUdfaSAtIFx0ZXh0cm17cmVsfVxoYXR7Rl9pfXwkCi0gJERfezEsIGl9ID0gfFx0ZXh0cm17cmVsfUcnX2kgLSBcdGV4dHJte3JlbH1caGF0e0ZfaX18JAotIFRoZSB0ZXN0IHN0YXRpc3RpYyBpcyBlaXRoZXIgJFxtYXgoRF97MCwgaX0pJCBvciAkXG1heChEX3sxLCBpfSkkLCB3aGljaGV2ZXIgbGVhZHMgdG8gdGhlIGhpZ2hlc3QgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlL3NtYWxsZXN0IHByb2JhYmlsaXR5LiBMb29rIHVwIHNpZ25pZmljYW5jZSBpbiB0YWJsZSBmcm9tIGFwcGVuZGl4LiBUaGUgMSBhbmQgMCBhcmUgdGhlICRcZGVsdGEkcy4gCgoKVGFibGVzIG9mIGNyaXRpY2FsIHZhbHVlcyB3ZXJlIGVudGVyZWQgYnkgaGFuZCBmcm9tIHRoZSBhcHBlbmRpeCB0byBaYXIgKDE5OTkpLgoKYGBge3IgYnNkcyBkZWx0YWtzIHRvIHVuaWZvcm0sIGVjaG8gPSBGfQpic2Rfa3NfdGVzdF9yYXcgPSBsYXBwbHkoYnNkcywgRlVOID0gemFyX2tzX3Rlc3QsIGRlbHRhX2NvcnJlY3Rpb24gPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgZm9jYWxfY29sdW1uID0gJ3NwZWNpZXNfbWVhbl9tYXNzJywKICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVjdGVkX3JhbmdlID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgIG5fb3JfaSA9ICduJywKICAgICAgICAgICAgICAgICAgICAgICAgIHN0b3JhZ2VwYXRoID0gc3RvcmFnZXBhdGgpCgpic2Rfa3NfdGVzdF9yYXdfcmVzdWx0cyA9IGRhdGEuZnJhbWUoCiAgY29tbXVuaXR5X25hbWUgPSBhcy5jaGFyYWN0ZXIobmFtZXMoYnNkX2tzX3Rlc3RfcmF3KSksCiAgc2lnbmlmID0gdmFwcGx5KGJzZF9rc190ZXN0X3JhdywgRlVOID0gZXh0cmFjdF92YWx1ZXNfemFya3MsIHZhbF9uYW1lID0gInNpZ25pZiIsIEZVTi5WQUxVRSA9IFRSVUUpLAogIHBfbWF4ID0gdmFwcGx5KGJzZF9rc190ZXN0X3JhdywgRlVOID0gZXh0cmFjdF92YWx1ZXNfemFya3MsIHZhbF9uYW1lID0gInBfbWF4IiwgRlVOLlZBTFVFID0gLjEpLCAKICBwX21pbiA9IHZhcHBseShic2Rfa3NfdGVzdF9yYXcsIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3phcmtzLCB2YWxfbmFtZSA9ICJwX21pbiIsIEZVTi5WQUxVRSA9IC4xKSwKICBkX3N0YXRpc3RpYyA9IHZhcHBseShic2Rfa3NfdGVzdF9yYXcsIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3phcmtzLCB2YWxfbmFtZSA9ICJkIiwgRlVOLlZBTFVFID0gLjEpLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGCikgJT4lIAogIGRwbHlyOjpsZWZ0X2pvaW4oZXJuZXN0X2JzZHNfdW5pZm9ybV9yZXN1bHRzLCBieSA9ICdjb21tdW5pdHlfbmFtZScpCgpwcmludChic2Rfa3NfdGVzdF9yYXdfcmVzdWx0cykKCmBgYAoKVGhlICRcZGVsdGEkIGNvcnJlY3RlZCBLUyB0ZXN0IGRvZXMgbm90IGNvcnJlc3BvbmQgdG8gdGhlIHJlc3VsdHMgZnJvbSBFcm5lc3Qgd2hlbiB0aGUgc3BlY2llcyBtZWFuIGJvZHkgc2l6ZSB2YWx1ZXMgYXJlIG9uIGFuIHVudHJhbnNmb3JtZWQgc2NhbGUuIAoKVXNpbmcgdGhlIG5hdHVyYWwgbG9nIG9mIHRoZSBzcGVjaWVzIG1lYW4gYm9keSBzaXplIHZhbHVlLCBob3dldmVyLi4uOgoKYGBge3IgYnNkcyBkZWx0YWtzIHRvIHVuaWZvcm0gbG9nLCBlY2hvID1GfQpic2Rfa3NfdGVzdF9sb2cgPSBsYXBwbHkoYnNkcywgRlVOID0gemFyX2tzX3Rlc3QsIGRlbHRhX2NvcnJlY3Rpb24gPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgZm9jYWxfY29sdW1uID0gJ2xuX21hc3MnLAogICAgICAgICAgICAgICAgICAgICAgICAgZXhwZWN0ZWRfcmFuZ2UgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgbl9vcl9pID0gJ24nLAogICAgICAgICAgICAgICAgICAgICAgICAgc3RvcmFnZXBhdGggPSBzdG9yYWdlcGF0aCkKCmJzZF9rc190ZXN0X2xvZ19yZXN1bHRzID0gZGF0YS5mcmFtZSgKICBjb21tdW5pdHlfbmFtZSA9IGFzLmNoYXJhY3RlcihuYW1lcyhic2Rfa3NfdGVzdF9sb2cpKSwKICBzaWduaWYgPSB2YXBwbHkoYnNkX2tzX3Rlc3RfbG9nLCBGVU4gPSBleHRyYWN0X3ZhbHVlc196YXJrcywgdmFsX25hbWUgPSAic2lnbmlmIiwgRlVOLlZBTFVFID0gVFJVRSksCiAgcF9tYXggPSB2YXBwbHkoYnNkX2tzX3Rlc3RfbG9nLCBGVU4gPSBleHRyYWN0X3ZhbHVlc196YXJrcywgdmFsX25hbWUgPSAicF9tYXgiLCBGVU4uVkFMVUUgPSAuMSksIAogIHBfbWluID0gdmFwcGx5KGJzZF9rc190ZXN0X2xvZywgRlVOID0gZXh0cmFjdF92YWx1ZXNfemFya3MsIHZhbF9uYW1lID0gInBfbWluIiwgRlVOLlZBTFVFID0gLjEpLAogIGRfc3RhdGlzdGljID0gdmFwcGx5KGJzZF9rc190ZXN0X2xvZywgRlVOID0gZXh0cmFjdF92YWx1ZXNfemFya3MsIHZhbF9uYW1lID0gImQiLCBGVU4uVkFMVUUgPSAuMSksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEYKKSAlPiUgCiAgZHBseXI6OmxlZnRfam9pbihlcm5lc3RfYnNkc191bmlmb3JtX3Jlc3VsdHMsIGJ5ID0gJ2NvbW11bml0eV9uYW1lJykKCnByaW50KGJzZF9rc190ZXN0X2xvZ19yZXN1bHRzKQpgYGAKCldpdGggbWVhbiBtYXNzIGxvZ2dlZCwgYWxsIHRoZSByZXN1bHRzIHJlcGxpY2F0ZSBxdWFsaXRhdGl2ZWx5IChpLmUuIG5vdCBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIHVuaWZvcm0pIGFuZCBOaXdvdCwgZm9yIHdoaWNoIHRoZSBjdXJyZW50bHktYXZhaWxhYmxlIGRhdGEgbW9zdCBjbG9zZWx5IG1hdGNoZXMgdGhhdCByZXBvcnRlZCBpbiBFcm5lc3QgKDIwMDUpLCByZXBsaWNhdGVzIGFsbW9zdCBleGFjdGx5IG51bWVyaWNhbGx5LiAKCiMjIyBDb21wYXJpbmcgQlNEcyBhbW9uZyBjb21tdW5pdGllcwoKIyMjIyBFcm5lc3QgYXBwcm9hY2gKCkVybmVzdCAoMjAwNSkgdXNlZCBhIHR3by1zYW1wbGUgS29sbW9nb3Jvdi1TbWlybm92IHRlc3QgdG8gY29tcGFyZSBldmVyeSBwb3NzaWJsZSBjb21iaW5hdGlvbiBvZiBjb21tdW5pdHktbGV2ZWwgQlNEcy4gCgpgYGB7ciBlcm5lc3Qga3MgdHdvIHNhbXBsZSByZXN1bHRzLCBlY2hvID1GfQphcHBlbmRpeF9iID0gdGlkeV9hcHBlbmRpeF9iKHN0b3JhZ2VwYXRoID0gc3RvcmFnZXBhdGgpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oZXJuZXN0X2tleSwgYnkgPSBjKCdzaXRlX2EnID0gJ3NpdGUnKSkgJT4lCiAgZHBseXI6OnJlbmFtZShjb21tdW5pdHlfYSA9IGNvbW11bml0eV9uYW1lKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKGVybmVzdF9rZXksIGJ5ID0gYygnc2l0ZV9iJyA9ICdzaXRlJykpICU+JQogIGRwbHlyOjpyZW5hbWUoY29tbXVuaXR5X2IgPSBjb21tdW5pdHlfbmFtZSkKCnByaW50KGFwcGVuZGl4X2IpCgpgYGAKCgoKIyMjIyBUcmFuc2xhdGlvbiB0byBgcmVwbGljYXRlLWJlY3NgCgpgYGB7ciBrcyB0d28gc2FtcGxlfQoKIyB1c2Ugc2FtZSBjb21tdW5pdHkgY29tYmluYXRpb25zIGFzIGJlZm9yZQoKYnNkX2Nyb3NzY29tbV9rcyA9IGxhcHBseShjb21tdW5pdHlfY29tYmluYXRpb25zLCBGVU4gPSBrc19ic2QsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGxuX21hc3NfdmFscyA9IEYpCgpgYGAKCmBgYHtyIHByaW50IGtzIHR3byBzYW1wbGUsIGVjaG8gPSBGfQpic2RfY3Jvc3Njb21tX3Jlc3VsdHMgPSBkYXRhLmZyYW1lKAogIGNvbW11bml0eV9hID0gdmFwcGx5KGJzZF9jcm9zc2NvbW1fa3MsIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3Rza3MsIHZhbF9uYW1lID0gImNvbW11bml0eV9hIiwgRlVOLlZBTFVFID0gJ3BvcnRhbCcpLAogIGNvbW11bml0eV9iID0gdmFwcGx5KGJzZF9jcm9zc2NvbW1fa3MsIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3Rza3MsIHZhbF9uYW1lID0gImNvbW11bml0eV9iIiwgRlVOLlZBTFVFID0gJ3BvcnRhbCcpLAogIGtzX2QgPSAgdmFwcGx5KGJzZF9jcm9zc2NvbW1fa3MsIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3Rza3MsIHZhbF9uYW1lID0gInN0YXRpc3RpYyIsIEZVTi5WQUxVRSA9IC41KSwKICAgIHBfdmFsdWUgPSAgdmFwcGx5KGJzZF9jcm9zc2NvbW1fa3MsIEZVTiA9IGV4dHJhY3RfdmFsdWVzX3Rza3MsIHZhbF9uYW1lID0gInAudmFsdWUiLCBGVU4uVkFMVUUgPSAuNSksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEYKKQoKCmJzZF9jcm9zc2NvbW1fam9pbmVkID0gYnNkX2Nyb3NzY29tbV9yZXN1bHRzICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oYXBwZW5kaXhfYiwgYnkgPSBjKCdjb21tdW5pdHlfYScsICdjb21tdW5pdHlfYicpKSAlPiUKICBuYS5vbWl0KCkKCmJzZF9jcm9zc2NvbW1fam9pbmVkMiA9IGJzZF9jcm9zc2NvbW1fcmVzdWx0cyAlPiUKICBkcGx5cjo6cmVuYW1lKGNvbW11bml0eV9iID0gY29tbXVuaXR5X2EsIGNvbW11bml0eV9hID0gY29tbXVuaXR5X2IpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oYXBwZW5kaXhfYiwgYnkgPSBjKCdjb21tdW5pdHlfYScsICdjb21tdW5pdHlfYicpKSAlPiUKICBuYS5vbWl0KCkgJT4lCiAgZHBseXI6OmJpbmRfcm93cyhic2RfY3Jvc3Njb21tX2pvaW5lZCkKCnByaW50KGJzZF9jcm9zc2NvbW1fam9pbmVkMikKCmBgYAoKYGBge3IgcGxvdCBic2QgY3Jvc3Njb21tIHB2YWwgaGlzdCwgZWNobyA9RiwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9IApic2RfcHZhbHNfaGlzdG9ncmFtID0gcGxvdF9jcm9zc2NvbW1fa3NfcHZhbHMoYnNkX2Nyb3NzY29tbV9qb2luZWQyKQoKaW52aXNpYmxlKGJzZF9wdmFsc19oaXN0b2dyYW0pCmBgYAoK